package com.ruoyi.factorydata.service.impl;

import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.ruoyi.common.utils.DateUtils;
import com.ruoyi.common.utils.SecurityUtils;
import com.ruoyi.common.utils.StringUtils;
import com.ruoyi.factorydata.domain.*;
import com.ruoyi.factorydata.domain.dto.MaterialRequisitionCodeDetailPrintDTO;
import com.ruoyi.factorydata.domain.dto.MaterialRequisitionCodePrintDTO;
import com.ruoyi.factorydata.mapper.MaterialRequisitionCodeDetialMapper;
import com.ruoyi.factorydata.mapper.MaterialRequisitionDetailMapper;
import com.ruoyi.factorydata.mapper.MaterialRequisitionMapper;
import com.ruoyi.factorydata.mapper.WorkOrderInfoMapper;
import com.ruoyi.factorydata.service.*;
import com.ruoyi.system.service.ISysUserService;
import org.apache.commons.lang3.time.DateFormatUtils;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.util.Assert;

import java.math.BigDecimal;
import java.util.*;

/**
 * 领料单Service业务层处理
 *
 * @author ruoyi
 * @date 2025-07-29
 */
@Service
public class MaterialRequisitionServiceImpl extends ServiceImpl<MaterialRequisitionMapper, MaterialRequisition> implements IMaterialRequisitionService {
    private final MaterialRequisitionMapper materialRequisitionMapper;

    private final WorkOrderInfoMapper workOrderInfoMapper;

    private final MaterialRequisitionDetailMapper materialRequisitionDetailMapper;

    private final ISysUserService sysUserService;

    private final IMaterialInventoryService materialInventoryService;

    private final IModelInventoryService modelInventoryService;
    private final IWorkOrderLogService workOrderLogService;
    private final IPrintTemplateService printTemplateService;
    private final MaterialRequisitionCodeDetialMapper materialRequisitionCodeDetialMapper;

    public MaterialRequisitionServiceImpl(MaterialRequisitionMapper materialRequisitionMapper,
                                          WorkOrderInfoMapper workOrderInfoMapper,
                                          MaterialRequisitionDetailMapper materialRequisitionDetailMapper,
                                          ISysUserService sysUserService, IMaterialInventoryService materialInventoryService,
                                          IModelInventoryService modelInventoryService,
                                          IWorkOrderLogService workOrderLogService, IPrintTemplateService printTemplateService,
                                          MaterialRequisitionCodeDetialMapper materialRequisitionCodeDetialMapper) {
        this.materialRequisitionMapper = materialRequisitionMapper;
        this.workOrderInfoMapper = workOrderInfoMapper;
        this.materialRequisitionDetailMapper = materialRequisitionDetailMapper;
        this.sysUserService = sysUserService;
        this.materialInventoryService = materialInventoryService;
        this.modelInventoryService = modelInventoryService;
        this.workOrderLogService = workOrderLogService;
        this.printTemplateService = printTemplateService;
        this.materialRequisitionCodeDetialMapper = materialRequisitionCodeDetialMapper;
    }


    /**
     * 查询领料单
     *
     * @param id 领料单主键
     * @return 领料单
     */
    @Override
    public MaterialRequisition selectMaterialRequisitionById(Long id) {
        MaterialRequisition materialRequisition = materialRequisitionMapper.selectMaterialRequisitionById(id);
        Map<Long, String> createByCache = new HashMap<>();
        WorkOrderInfo workOrderInfo = workOrderInfoMapper.selectWorkOrderInfoById(materialRequisition.getWorkOrderId());
        String requisitionUserName = sysUserService.getUserNameByIdAndCache(materialRequisition.getRequisitionUserId(), createByCache);
        List<MaterialRequisitionDetail> materialRequisitionDetails = materialRequisitionDetailMapper.selectList(new QueryWrapper<MaterialRequisitionDetail>().lambda()
                .eq(MaterialRequisitionDetail::getRequisitionId, materialRequisition.getId()));
        BigDecimal total = materialRequisitionDetails.stream()
                .map(MaterialRequisitionDetail::getRequiredQuantity) // 提取字段
                .reduce(BigDecimal.ZERO, BigDecimal::add); // 安全求和
        materialRequisition.setRequisitionQuantity(total.longValue());
        materialRequisition.setWorkOrderCode(workOrderInfo.getWorkOrderCode());
        materialRequisition.setRequisitionUserName(requisitionUserName);
        String updateByName = materialRequisition.getUpdateBy() == null ? null : sysUserService.getUserNameByIdAndCache(Long.parseLong(materialRequisition.getUpdateBy()), createByCache);
        materialRequisition.setUpdateByName(updateByName);
        return materialRequisition;
    }

    /**
     * 查询领料单列表
     *
     * @param materialRequisition 领料单
     * @return 领料单
     */
    @Override
    public List<MaterialRequisition> selectMaterialRequisitionList(MaterialRequisition materialRequisition) {
        List<MaterialRequisition> materialRequisitions = materialRequisitionMapper.selectMaterialRequisitionList(materialRequisition);
        Map<Long, String> createByCache = new HashMap<>();
        materialRequisitions.forEach(item -> {
            WorkOrderInfo workOrderInfo = workOrderInfoMapper.selectWorkOrderInfoById(item.getWorkOrderId());
            String requisitionUserName = sysUserService.getUserNameByIdAndCache(item.getRequisitionUserId(), createByCache);
            List<MaterialRequisitionDetail> materialRequisitionDetails = materialRequisitionDetailMapper.selectList(new QueryWrapper<MaterialRequisitionDetail>().lambda()
                    .eq(MaterialRequisitionDetail::getRequisitionId, item.getId()));
            BigDecimal total = materialRequisitionDetails.stream()
                    .map(MaterialRequisitionDetail::getRequiredQuantity) // 提取字段
                    .reduce(BigDecimal.ZERO, BigDecimal::add); // 安全求和
            item.setRequisitionQuantity(total.longValue());
            item.setWorkOrderCode(workOrderInfo.getWorkOrderCode());
            item.setRequisitionUserName(requisitionUserName);
            String updateByName = item.getUpdateBy() == null ? null : sysUserService.getUserNameByIdAndCache(Long.parseLong(item.getUpdateBy()), createByCache);
            item.setUpdateByName(updateByName);
        });
        return materialRequisitions;
    }

    /**
     * 新增领料单
     *
     * @param materialRequisition 领料单
     * @return 结果
     */
    @Override
    public int insertMaterialRequisition(MaterialRequisition materialRequisition) {
        materialRequisition.setCreateTime(DateUtils.getNowDate());
        return materialRequisitionMapper.insertMaterialRequisition(materialRequisition);
    }

    /**
     * 修改领料单
     *
     * @param materialRequisition 领料单
     * @return 结果
     */
    @Override
    public int updateMaterialRequisition(MaterialRequisition materialRequisition) {
        materialRequisition.setUpdateTime(DateUtils.getNowDate());
        return materialRequisitionMapper.updateMaterialRequisition(materialRequisition);
    }

    /**
     * 批量删除领料单
     *
     * @param ids 需要删除的领料单主键
     * @return 结果
     */
    @Override
    public int deleteMaterialRequisitionByIds(Long[] ids) {
        return materialRequisitionMapper.deleteMaterialRequisitionByIds(ids);
    }

    /**
     * 删除领料单信息
     *
     * @param id 领料单主键
     * @return 结果
     */
    @Override
    public int deleteMaterialRequisitionById(Long id) {
        return materialRequisitionMapper.deleteMaterialRequisitionById(id);
    }

    @Override
    @Transactional
    public int refuseRequest(MaterialRequisition materialRequisition) {
        MaterialRequisition requisition = selectMaterialRequisitionById(materialRequisition.getId());
        Assert.isTrue(requisition != null, "当前领料单不存在");
        Assert.isTrue(requisition.getStatus() == 0, "当前状态无法拒绝");
        MaterialRequisition update = new MaterialRequisition();
        update.setId(materialRequisition.getId());
        update.setStatus(-1);
        update.setRemark(materialRequisition.getRemark());
        update.setUpdateTime(DateUtils.getNowDate());
        update.setUpdateBy(SecurityUtils.getUserId() + "");
        int opt = updateMaterialRequisition(update);
        if (opt > 0) {
            WorkOrderInfo workOrderInfo = workOrderInfoMapper.selectWorkOrderInfoById(requisition.getWorkOrderId());
            Assert.notNull(workOrderInfo, "工单不存在");
            String refuse = materialRequisition.getRemark();
            refuse = StringUtils.isBlank(refuse) ? "无" : refuse;
            workOrderLogService.saveLog(workOrderInfo, "工单领料单号：" + requisition.getRequisitionNo() + "申请被拒，原因：" + refuse);
        }
        return opt;
    }

    @Override
    @Transactional
    public int authRequest(MaterialRequisition materialRequisition) {
        MaterialRequisition requisition = selectMaterialRequisitionById(materialRequisition.getId());
        Assert.isTrue(requisition != null, "当前领料单不存在");
        Assert.isTrue(requisition.getStatus() == 0, "当前状态无法操作");
        materialRequisition.setStatus(1);
        materialRequisition.setUpdateTime(DateUtils.getNowDate());
        materialRequisition.setUpdateBy(SecurityUtils.getUserId() + "");
        int update = updateMaterialRequisition(materialRequisition);
        if (update > 0) {
            List<MaterialRequisitionDetail> materialRequisitionDetailList = materialRequisition.getMaterialRequisitionDetailList();
            for (MaterialRequisitionDetail detail : materialRequisitionDetailList) {
                BigDecimal receivedQuantity = BigDecimal.ZERO;
                //批次编号审批：物料批次编号|出库数量,母版批次编号|出库数量
                if (StringUtils.isNotBlank(detail.getBatchNumberStr())) {
                    String[] batchArr = detail.getBatchNumberStr().split(",");
                    for (String batch : batchArr) {
                        String[] batchNumberArr = batch.split("\\|");
                        String inventoryBatchNumber = batchNumberArr[0];
                        BigDecimal inventoryQuantity = new BigDecimal(batchNumberArr[1]);
                        receivedQuantity = receivedQuantity.add(inventoryQuantity);
                        //扣除对应物料库存
                        detail.setRemark(materialRequisition.getRemark());
                        if (detail.getType() == 1) {
                            materialInventoryService.saveInventoryOut(detail, inventoryBatchNumber, inventoryQuantity);
                        } else {
                            //扣除母版库存
                            modelInventoryService.saveInventoryOut(detail, inventoryBatchNumber, inventoryQuantity);
                        }
                    }
                }
                detail.setUpdateBy(SecurityUtils.getUserId() + "");
                detail.setUpdateTime(DateUtils.getNowDate());
                detail.setBatchNumber(detail.getBatchNumberStr());
                detail.setReceivedQuantity(receivedQuantity);
                detail.setShortageQuantity(receivedQuantity.compareTo(detail.getRequiredQuantity()) >= 0 ? BigDecimal.ZERO : detail.getRequiredQuantity().subtract(receivedQuantity));
                update = materialRequisitionDetailMapper.updateMaterialRequisitionDetail(detail);
                Assert.isTrue(update > 0, "领料单明细保存失败");
            }
            WorkOrderInfo workOrderInfo = workOrderInfoMapper.selectWorkOrderInfoById(requisition.getWorkOrderId());
            Assert.notNull(workOrderInfo, "工单不存在");
            workOrderLogService.saveLog(workOrderInfo, "工单领料单号：" + requisition.getRequisitionNo() + "申请通过");
        }

        return update;
    }

    @Override
    @Transactional
    public int returnRequest(MaterialRequisition materialRequisition) {
        MaterialRequisition requisition = selectMaterialRequisitionById(materialRequisition.getId());
        Assert.isTrue(requisition != null, "当前领料单不存在");
        Assert.isTrue(requisition.getStatus() >= 1, "当前状态无法操作");
        Long workOrderId = requisition.getWorkOrderId();
        WorkOrderInfo workOrderInfo = workOrderInfoMapper.selectWorkOrderInfoById(workOrderId);
        Assert.notNull(workOrderInfo, "当前工单不存在");
        requisition.setStatus(4);//退回
        requisition.setUpdateBy(SecurityUtils.getUserId() + "");
        requisition.setUpdateTime(DateUtils.getNowDate());
        int update = updateMaterialRequisition(requisition);
        if (update > 0) {
            BigDecimal applyReturnedQuantity = BigDecimal.ZERO;
            //处理退料
            List<MaterialRequisitionDetail> materialRequisitionDetails = materialRequisitionDetailMapper.selectMaterialRequisitionDetailList(MaterialRequisitionDetail.builder().requisitionId(materialRequisition.getId()).build());
            Assert.notNull(materialRequisitionDetails, "当前领料单无明细");
            for (MaterialRequisitionDetail detail : materialRequisitionDetails) {
                if (detail.getApplyReturnedQuantity() == null || detail.getApplyReturnedQuantity().compareTo(BigDecimal.ZERO) == 0) {
                    continue;
                }
                //本次退还库存
                BigDecimal returnedQuantity = detail.getApplyReturnedQuantity();
                //剩余待退还库存
                BigDecimal remainInventory = returnedQuantity;
                detail.setUpdateBy(SecurityUtils.getUserId() + "");
                detail.setUpdateTime(DateUtils.getNowDate());
                detail.setApplyReturnedQuantity(BigDecimal.ZERO);//清空当前申请
                update = materialRequisitionDetailMapper.updateMaterialRequisitionDetail(detail);
                Assert.isTrue(update > 0, "退料保存失败");

                //批次编号审批：物料批次编号|出库数量,母版批次编号|出库数量
                if (StringUtils.isNotBlank(detail.getBatchNumber())) {
                    String[] batchArr = detail.getBatchNumber().split(",");
                    for (String batch : batchArr) {
                        //还有待退还的
                        if (remainInventory.compareTo(BigDecimal.ZERO) > 0) {
                            String[] batchNumberArr = batch.split("\\|");
                            String inventoryBatchNumber = batchNumberArr[0];
                            BigDecimal inventoryQuantity = new BigDecimal(batchNumberArr[1]);
                            //退还数量是否超过当前批次数量
                            BigDecimal returnInventory = BigDecimal.ZERO;
                            //批次出库数量大于等于当前退还数量
                            if (inventoryQuantity.compareTo(returnedQuantity) >= 0) {
                                returnInventory = returnedQuantity;
                                remainInventory = BigDecimal.ZERO;
                            } else {
                                returnInventory = inventoryQuantity;
                                remainInventory = remainInventory.subtract(inventoryQuantity);
                            }
                            //退还对应物料库存
                            if (detail.getType() == 1) {
                                materialInventoryService.saveInventoryIn(detail, inventoryBatchNumber, returnInventory);
                            } else {
                                modelInventoryService.saveInventoryIn(detail, inventoryBatchNumber, returnInventory);
                            }
                        }
                    }
                }
                applyReturnedQuantity = applyReturnedQuantity.add(returnedQuantity);

            }
            workOrderLogService.saveLog(workOrderInfo, "工单申请退料，领料单：" + requisition.getRequisitionNo() + ",总退料数量：" + applyReturnedQuantity.longValue());

        }
        return update;
    }


    @Override
    public int returnRefuseMaterial(MaterialRequisition materialRequisition) {
        MaterialRequisition requisition = selectMaterialRequisitionById(materialRequisition.getId());
        Assert.isTrue(requisition != null, "当前领料单不存在");
        Assert.isTrue(requisition.getStatus() >= 1, "当前状态无法操作");
        Long workOrderId = requisition.getWorkOrderId();
        WorkOrderInfo workOrderInfo = workOrderInfoMapper.selectWorkOrderInfoById(workOrderId);
        Assert.notNull(workOrderInfo, "当前工单不存在");
        requisition.setStatus(4);//退回
        requisition.setUpdateBy(SecurityUtils.getUserId() + "");
        requisition.setUpdateTime(DateUtils.getNowDate());
        int update = updateMaterialRequisition(requisition);
        if (update > 0) {
            //处理退料
            List<MaterialRequisitionDetail> materialRequisitionDetails = materialRequisitionDetailMapper.selectMaterialRequisitionDetailList(MaterialRequisitionDetail.builder().requisitionId(materialRequisition.getId()).build());
            Assert.notNull(materialRequisitionDetails, "当前领料单无明细");
            for (MaterialRequisitionDetail detail : materialRequisitionDetails) {
                if (detail.getApplyReturnedQuantity() == null || detail.getApplyReturnedQuantity().compareTo(BigDecimal.ZERO) == 0) {
                    continue;
                }
                detail.setUpdateBy(SecurityUtils.getUserId() + "");
                detail.setUpdateTime(DateUtils.getNowDate());
                detail.setReturnedQuantity(detail.getReturnedQuantity().subtract(detail.getApplyReturnedQuantity()));
                detail.setApplyReturnedQuantity(BigDecimal.ZERO);
                update = materialRequisitionDetailMapper.updateMaterialRequisitionDetail(detail);
                Assert.isTrue(update > 0, "退料保存失败");
            }
            String remark = StringUtils.isBlank(materialRequisition.getRemark()) ? "无" : materialRequisition.getRemark();
            workOrderLogService.saveLog(workOrderInfo, "工单申请退料被拒，领料单：" + requisition.getRequisitionNo() + ",拒绝原因：" + remark);
        }
        return update;
    }

    @Override
    @Transactional
    public MaterialRequisitionCodePrintDTO print(MaterialRequisition materialRequisition) {
        MaterialRequisition requisition = selectMaterialRequisitionById(materialRequisition.getId());
        Assert.isTrue(requisition != null, "当前领料单不存在");
        List<MaterialRequisitionDetail> materialRequisitionDetails = materialRequisitionDetailMapper.selectMaterialRequisitionDetailList(MaterialRequisitionDetail.builder().requisitionId(materialRequisition.getId()).build());
        Assert.notNull(materialRequisitionDetails, "当前领料单无明细");

        Date requisitionTime =requisition.getRequisitionTime()==null?requisition.getCreateTime():requisition.getRequisitionTime();
        String materialRequisitionCode = DateFormatUtils.format(requisitionTime, "yyyyMMdd");
        //删除之前的数据
        materialRequisitionCodeDetialMapper.deleteMaterialRequisitionCodeDetialByRequisitionId(requisition.getId());

        List<MaterialRequisitionCodeDetailPrintDTO> detailPrintDTOList =new ArrayList<>();

        for (MaterialRequisitionDetail detail:materialRequisitionDetails) {
            if (detail.getReceivedQuantity() == null
                    || detail.getReceivedQuantity().compareTo(BigDecimal.ZERO) == 0
                    || detail.getType() == 2) {
                continue;
            }

            String[] split = detail.getBatchNumber().split(",");
            for (String batchNumberStr:split) {
                String[] batchNumberArr = batchNumberStr.split("\\|");
                String batchNum = batchNumberArr[0];
                String inventoryNum = batchNumberArr[1];
                if(Integer.parseInt(inventoryNum)<=0){
                    continue;
                }
                //查询入库时间
                MaterialInventory materialInventory = materialInventoryService.lambdaQuery()
                        .eq(MaterialInventory::getBatchNumber, batchNum)
                        .eq(MaterialInventory::getMaterialCode, detail.getMaterialCode())
                        .select(MaterialInventory::getReceiptDate)
                        .last("limit 1")
                        .one();
                String receiptDate =Optional.ofNullable(materialInventory).map(ite-> DateFormatUtils.format(materialInventory.getReceiptDate(), "yyyy/MM/dd")).orElse("-");
                for (int i = 0; i < Integer.parseInt(inventoryNum); i++) {
                    Integer maxSlot =materialRequisitionCodeDetialMapper.selectMaxSlot(materialRequisitionCode);
                    maxSlot= Optional.ofNullable(maxSlot).orElse(99);
                    Integer finalMaxSlot = maxSlot+1;

                    MaterialRequisitionCodeDetial requisitionCodeDetial = MaterialRequisitionCodeDetial.builder()
                            .requisitionId(detail.getRequisitionId())
                            .requisitionNo(detail.getRequisitionNo())
                            .projectId(detail.getProjectId())
                            .planId(detail.getPlanId())
                            .workOrderId(detail.getWorkOrderId())
                            .batchNumber(batchNum)
                            .materialId(detail.getMaterialId())
                            .materialCode(detail.getMaterialCode())
                            .materialName(detail.getMaterialName())
                            .materialType(detail.getMaterialType())
                            .materialSpecification(detail.getMaterialSpecification())
                            .materialUnit(detail.getMaterialUnit())
                            .materialUnitName(detail.getMaterialUnitName())
                            .materialCategoryId(detail.getMaterialCategoryId())
                            .manufacturingMethod(detail.getManufacturingMethod())
                            .receiptDate(Optional.ofNullable(materialInventory).map(MaterialInventory::getReceiptDate).orElse(null))
                            .materialRequisitionCode(materialRequisitionCode)
                            .materialRequisitionCodeSlot(finalMaxSlot+"")
                            .build();
                    requisitionCodeDetial.setCreateBy(SecurityUtils.getUserId() + "");
                    requisitionCodeDetial.setCreateTime(DateUtils.getNowDate());
                    materialRequisitionCodeDetialMapper.insertMaterialRequisitionCodeDetial(requisitionCodeDetial);
                    detailPrintDTOList.add(MaterialRequisitionCodeDetailPrintDTO.builder()
                            .index(requisitionCodeDetial.getMaterialRequisitionCodeSlot())
                            .materialName(detail.getMaterialName())
                            .expandParams(detail.getMaterialSpecification())
                            .receiptDate(receiptDate)
                            .batchNumber(batchNum)
                            .barcode(requisitionCodeDetial.getMaterialRequisitionCode()+requisitionCodeDetial.getMaterialRequisitionCodeSlot())
                            .barcodeValue(requisitionCodeDetial.getMaterialRequisitionCode()+requisitionCodeDetial.getMaterialRequisitionCodeSlot())
                            .build()) ;
                }
            }
        }

        PrintTemplate printTemplate = printTemplateService
                .lambdaQuery()
                .eq(PrintTemplate::getPrintType, "materialInfo")
                .orderByDesc(PrintTemplate::getId)
                .last(" limit 1")
                .one();
        Assert.notNull(printTemplate, "打印模板不存在");
        return MaterialRequisitionCodePrintDTO.builder()
                .templateName(printTemplate.getPrintType())
                .template(printTemplate.getContent())
                .detailTable(detailPrintDTOList)
                .build();
    }
}